/*******************************************************************************
 * Copyright (c) 2002 Boris Pruessmann and others.
 * All rights reserved.   This program and the accompanying materials
 * are made available under the terms of the Common Public License v0.5
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v05.html
 * 
 * Contributors:
 *    Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.ui.internal.model;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;

import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.IChangesResult;
import net.sourceforge.perforce.core.api.IOpenedResult;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.api.IResolveResult;
import net.sourceforge.perforce.core.api.IResolvedResult;
import net.sourceforge.perforce.core.resources.IChangelist;
import net.sourceforge.perforce.core.resources.IP4File;
import net.sourceforge.perforce.ui.PerforceUIPlugin;

/** This class performs caching for the PendingChangelistView.
  * 
  * @version $Revision: 252 $
  * @author  <a href="mailto:bpruessmann@users.sourceforge.net">Boris Pruessmann</a>
  */
public class PendingChangelistCache 
{	
	// --------------------------------------------------------------------------- Private Variables

	/** Stores the Perforce server instance. */
	private final IPerforceServer server;
	/** Stores the changelist cache. */
	private final Map changelistCache = new HashMap();
	/** Stores the file cache. */
	private final Map fileCache = new HashMap();
	/** Indicates whether the cache is valid or not. */
	private boolean isValid = false;
	/** Indicates whether when have to collect all opened info. */
	private boolean performOpenedAll = false;
		
	// ------------------------------------------------------------------------- Public Constructors
	
	/** Constructor for PendingChangelistCache.
	  *
	  * @param theServer the Perforce server. 
	  */
	public PendingChangelistCache(IPerforceServer theServer)
	{
		server = theServer;
		
		changelistCache.put(
			createKey(IChangelist.DEFAULT), 
			new DefaultChangelistElement(theServer, this));
	}
	
	// ------------------------------------------------------------------------------ Public Methods
	
	/** Sets the 'performOpenedAll' value.
	  *
	  * @param newPerformOpenedAll the new value. 
	  */
	public void setPerformOpenedAll(boolean newPerformOpenedAll)
	{
		performOpenedAll = newPerformOpenedAll;
	}
	
	/** Called to retrieve the changelist elements.
	  *
	  * @param comparator used for selecting items from the cache.
	  * @return array of changelist elements. 
	  */
	public synchronized ChangelistElement[] getChangelistElements(Comparator comparator)
	{
		refreshCache();		
		
		ArrayList result = new ArrayList();
		for (Iterator it = changelistCache.values().iterator(); it.hasNext();)
		{
			Object o = it.next();
			if (comparator.equals(o))
			{
				result.add(o);
			}
		}
		
		return (ChangelistElement[]) result.toArray(new ChangelistElement[result.size()]);
	}
	
	/** Called to retrieve the file elements.
	  *
	  * @param comparator used for selecting items from the cache.
	  * @return array of file elements. 
	  */
	public synchronized IP4File[] getFileElements(Comparator comparator)
	{
		refreshCache();
		
		ArrayList result = new ArrayList();
		for (Iterator it = fileCache.values().iterator(); it.hasNext();)
		{
			Object o = it.next();
			if (comparator.equals(o))
			{
				result.add(o);
			}
		}
		
		return (IP4File[]) result.toArray(new IP4File[result.size()]);
	}
	
	/** Called to invalidate the cache. */
	public synchronized void invalidate()
	{
		isValid = false;
	}
	
	// ----------------------------------------------------------------------------- Private Methods
	
	/** Called to refresh part of the cache. */
	private void refreshCache()
	{
		if (isValid)
		{
			return;
		}
		
		try
		{
			HashSet fileSet = new HashSet();
			
			HashSet changelistSet = new HashSet();
			changelistSet.add(createKey(IChangelist.DEFAULT));
			
			// Gather infos
			IOpenedResult[] openedResults = server.opened(performOpenedAll);
			IResolveResult[] resolveResults = 
				server.resolve(
					null,
					null, 
					IPerforceServer.RESOLVE_SIMULATE);
			IResolvedResult[] resolvedResults = server.resolved(null);
			IChangesResult[] changesResults = 
				server.changes(IChangelist.STATUS_PENDING, Integer.MAX_VALUE);
				
			// Process changesResults
			for (int i = 0; i < changesResults.length; i++)
			{
				IChangesResult changesResult = changesResults[i];
				String key = 
					createKey(changesResult.getChangelist(), changesResult.getClientName());
				changelistSet.add(key);
				
				
				ChangelistElement changelist = (ChangelistElement) changelistCache.get(key); 
				if (changelist == null)
				{
					changelist = new ChangelistElement(server, this, changesResult.getChangelist());
					changelistCache.put(key, changelist);
				}
				
				changelist.setUserName(changesResult.getUserName());
				changelist.setClientName(changesResult.getClientName());
				changelist.setDate(changesResult.getDate());
				changelist.setDescription(changesResult.getDescription());				
			}
			
			// process openedResults
			for (int i = 0; i < openedResults.length; i++)
			{
				IOpenedResult openedResult = openedResults[i];
				String key = 
					createKey(openedResult.getDepotFilename(), openedResult.getClientName());
				fileSet.add(key);
				
				P4File file = (P4File) fileCache.get(key); 
				if (file == null)
				{
					file = new P4File(server);
					fileCache.put(key, file);
				}
				
				if (openedResult.getChangelist().equals(IChangelist.DEFAULT) &&
				    !openedResult.getClientName().equals(server.getClient()))
				{
					ChangelistElement changelist = 
						new ChangelistElement(server, this, IChangelist.DEFAULT);
						
					changelist.setUserName(openedResult.getUserName());
					changelist.setClientName(openedResult.getClientName());
					changelist.setDate(new Date());
					changelist.setDescription("");                                     //$NON-NLS-1$
						
					key = createKey(changelist.getId(), changelist.getClientName());
					changelistCache.put(key, changelist);
					changelistSet.add(key);
				}
				
				file.setChangelistId(openedResult.getChangelist());
				file.setAction(openedResult.getAction());
				file.setDepotFilename(openedResult.getDepotFilename());
				file.setClientName(openedResult.getClientName());
				file.setRevision(openedResult.getRevision());
				file.setOurLock(openedResult.hasOurLock());
				file.setNeedsResolve(false);
			}
			
			// process resolveResults
			for (int i = 0; i < resolveResults.length; i++)
			{
				IResolveResult resolveResult = resolveResults[i];
				String key = 
					createKey(resolveResult.getDepotFilename(), server.getClient());
					
				P4File file = (P4File) fileCache.get(key);
				if (file != null)
				{
					file.setNeedsResolve(true);
				}
			}
			
			// process resolvedResults
			for (int i = 0; i < resolvedResults.length; i++)
			{
				IResolvedResult resolvedResult = resolvedResults[i];
				String key = 
					createKey(resolvedResult.getDepotFilename(), server.getClient());
					
				P4File file = (P4File) fileCache.get(key);
				if (file != null)
				{
					file.setResolved(true);
				}
			}
			
			// Removed changelists that don't exist anymore
			for (Iterator it = changelistCache.entrySet().iterator(); it.hasNext();)
			{
				Map.Entry entry = (Map.Entry) it.next();
				if (!changelistSet.contains(entry.getKey()))
				{
					it.remove();
				}
			}
			
			// Remove files that don't exist anymore
			for (Iterator it = fileCache.entrySet().iterator(); it.hasNext();)
			{
				Map.Entry entry = (Map.Entry) it.next();
				if (!fileSet.contains(entry.getKey()))
				{
					it.remove();
				}
			}
			
		}
		catch (PerforceException e)
		{
			PerforceUIPlugin.log(e.getStatus());
		}
		finally
		{
			isValid = true;
		}
	}
	
	/** Creates a key for the changelist cahce.
	  *
	  * @param id the changelist id.
	  * @return the generated key. 
	  */
	private String createKey(Integer id)
	{
		return createKey(id, server.getClient());
	}
	
	/** Creates a key for the changelist cahce.
	  *
	  * @param id the changelist id. 
	  * @param clientName the client name
	  * @return the generated key.
	  */
	private String createKey(Integer id, String clientName)
	{
		return createKey(id.toString(), clientName);
	}
	
	/** Creates a key for the changelist cahce.
	  *
	  * @param text some misc. text. 
	  * @param clientName the client name
	  * @return the generated key.
	  */
	private String createKey(String text, String clientName)
	{
		return text + '@' + clientName;
	}
}
